﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace StyleMVVM.Data.Collections
{
	public class ComplexList<T> : ComplexObject, IComplexList<T>
	{
		private readonly ObservableCollection<T> internalCollection;
		private bool tIsComplex = false;
		private bool pauseCollectionChanged;

		public ComplexList()
		{
			internalCollection = new ObservableCollection<T>();
			internalCollection.CollectionChanged += InternalCollectionOnCollectionChanged;

			GetIfTComplex();
		}

		public ComplexList(IEnumerable<T> list)
		{
			internalCollection = new ObservableCollection<T>();
			internalCollection.CollectionChanged += InternalCollectionOnCollectionChanged;

			GetIfTComplex();

			AddRange(list);
		}

		public IEnumerator<T> GetEnumerator()
		{
			return internalCollection.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		public void Add(T item)
		{
			internalCollection.Add(item);

			IComplexObject complex = item as IComplexObject;

			if (complex != null)
			{
				complex.SetParent(this, "Item", childDataChange);
			}
		}

		public void AddRange(IEnumerable<T> range)
		{
			pauseCollectionChanged = true;

			foreach (T complexO in range)
			{
				Add(complexO);
			}

			pauseCollectionChanged = false;

			OnCollectionChanged(
				new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,
				                                     new ComplexList<T>(range)));
		}

		public int Add(object value)
		{
			Add((T)value);

			return 1;
		}

		public void Clear()
		{
			if (tIsComplex)
			{
				foreach (T complexO in internalCollection)
				{
					((IComplexObject)complexO).SetParent(null, null, null);
				}
			}

			internalCollection.Clear();
		}

		public bool Contains(object value)
		{
			return Contains((T)value);
		}

		public int IndexOf(object value)
		{
			return IndexOf((T)value);
		}

		public void Insert(int index, object value)
		{
			Insert(index, (T)value);
		}

		public void Remove(object value)
		{
			Remove((T)value);
		}

		public bool Contains(T item)
		{
			return internalCollection.Contains(item);
		}

		public void CopyTo(T[] array, int arrayIndex)
		{
			internalCollection.CopyTo(array, arrayIndex);
		}

		public bool Remove(T item)
		{
			IComplexObject complexObject = item as IComplexObject;

			if (internalCollection.Remove(item))
			{
				if (complexObject != null)
				{
					complexObject.SetParent(null, null, null);
				}

				return true;
			}
			return false;
		}

		public void CopyTo(Array array, int index)
		{
			((IList)internalCollection).CopyTo(array, index);
		}

		public int Count
		{
			get { return internalCollection.Count; }
		}

		public bool IsSynchronized
		{
			get { return false; }
		}

		public object SyncRoot
		{
			get { return null; }
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		object IList.this[int index]
		{
			get { return this[index]; }
			set { this[index] = (T)value; }
		}

		public int IndexOf(T item)
		{
			return internalCollection.IndexOf(item);
		}

		public void Insert(int index, T item)
		{
			internalCollection.Insert(index, item);

			IComplexObject complex = item as IComplexObject;

			if (complex != null)
			{
				complex.SetParent(this, "Item", childDataChange);
			}
		}

		public void RemoveAt(int index)
		{
			internalCollection.RemoveAt(index);
		}

		public bool IsFixedSize { get; private set; }

		public T this[int index]
		{
			get { return internalCollection[index]; }
			set
			{
				IComplexObject oldObject = null;

				if (index < Count)
				{
					oldObject = internalCollection[index] as IComplexObject;
				}
				IComplexObject newObject = value as IComplexObject;

				internalCollection[index] = value;

				if (oldObject != null)
				{
					oldObject.SetParent(null, null, null);
				}

				if (newObject != null)
				{
					newObject.SetParent(this, "Item", childDataChange);
				}
			}
		}

		public event NotifyCollectionChangedEventHandler CollectionChanged;

		protected void GetIfTComplex()
		{
			foreach (Type implementedInterface in typeof(T).GetTypeInfo().ImplementedInterfaces)
			{
				if (implementedInterface == typeof(IComplexObject))
				{
					tIsComplex = true;
					break;
				}
			}
		}

		private void InternalCollectionOnCollectionChanged(object sender,
		                                                   NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
		{
			OnCollectionChanged(notifyCollectionChangedEventArgs);
		}

		private void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
		{
			NotifyCollectionChangedEventHandler handler = CollectionChanged;

			if (handler != null && !pauseCollectionChanged)
			{
				handler(this, args);
			}
		}
	}
}